package dom.xpath.query.xpath.expr;

import dom.xpath.query.EvalException;
import dom.xpath.query.xpath.XPContext;
import dom.xpath.query.xpath.values.Item;
import dom.xpath.query.xpath.values.NodeSet;
import dom.xpath.util.Array;

/**
 * FTIntersection Expression. This expresses the intersection
 *  of two FTContains results.
 * 
 * @author Workgroup DBIS, University of Konstanz 2005-07, ISC License
 * @author Sebastian Gath
 */
public final class FTIntersection extends ArrayExpr {
  /**
   * Constructor.
   * @param e operands joined with the union operator
   */
  public FTIntersection(final Expression[] e) {
    exprs = e;
  }

  @Override
  public NodeSet eval(final XPContext ctx) throws EvalException {
    int[][] res = null;
    int[][] tmp;
      
    Item it = exprs[0].eval(ctx);
    if(it instanceof NodeSet) {
      res = ((NodeSet) it).ftidpos;
   
      for (int i = 1; i < exprs.length; i++) {
        it = exprs[i].eval(ctx);
        if (it instanceof NodeSet) {
          tmp = ((NodeSet) it).ftidpos;
          res = calculateFTAnd(res, tmp);
        }
      }
      return new NodeSet(Array.extractIDsFromData(res), ctx, res);
    }
    return null;
  }

  @Override
  public Expression compile(final XPContext ctx) throws EvalException {
    final int el = exprs.length;
    for(int e = 0; e != el; e++) exprs[e] = exprs[e].compile(ctx);
    return this;
  }
  
  /**
   * Built join for value1 and value2; used key is id.
   * @param val1 input set int[][]
   * @param val2 input set  int[][]
   * @return result int[][]
   */
  public int[][] calculateFTAnd(final int[][] val1, final int[][] val2) {
    int lastId = -1;
    int[][] values1 = val1;
    int[][] values2 = val2;

    if(values1 == null || values1[0].length == 0 || values2 == null
        || values2[0].length == 0) {
      return null;
    }

    // calculate minimum size
    int min = Math.min(values1[0].length, values2[0].length);
    int[][] maxResult;
    // double space, because 2 values for each identical id
    maxResult = new int[2][values1[0].length + values2[0].length];

    //if (min == values2.length && min != values1.length) {
    if(min == values2[0].length && min != values1[0].length) {

      // change arrays
      int[][] tmp = values2;
      values2 = values1;
      values1 = tmp;
      //changedOrder = true;
    }

    // run variable for values1
    int i = 0;
    // run variable for values2
    int k = 0;
    // count added elements
    int counter = 0;
    int cmpResult;

    // each value from the smaller set are compared with the bigger set and
    // added to result
    while(i < values1[0].length && k < values2[0].length) {
      cmpResult = Array.compareIntArrayEntry(values1[0][i], values1[1][i],
          values2[0][k], values2[1][k]);
      if(cmpResult == -1) {
        // same Id, but pos1 < pos 2 values1[i] < values2[k]
        maxResult[0][counter] = values1[0][i];
        maxResult[1][counter] = values1[1][i];

        lastId = values1[0][i];
        counter++;

        i++;
      } else if(cmpResult == -2) {
        // id1 < i2
        if(lastId != values1[0][i]) {

          i++;
        } else {
          // same value and Id == lastId have to bi copied
          while(i < values1[0].length && lastId == values1[0][i]) {

            maxResult[0][counter] = values1[0][i];
            maxResult[1][counter] = values1[1][i];

            counter++;
            i++;
          }
        }
      } else if(cmpResult == 2) {
        // id1 > i2
        if(lastId != values2[0][k]) {

          k++;
        } else {
          // all values with same Id == lastId have to bi copied
          while(k < values2[0].length && lastId == values2[0][k]) {

            maxResult[0][counter] = values2[0][k];
            maxResult[1][counter] = values2[1][k];

            counter++;
            k++;
          }
        }
      } else if(cmpResult == 1) {
        // same ids, aber pos1 > pos2 values1[i] > values2[k]
        maxResult[0][counter] = values2[0][k];
        maxResult[1][counter] = values2[1][k];

        lastId = values2[0][k];
        counter++;
        k++;
      } else {
        // entry identical
        maxResult[0][counter] = values2[0][k];
        maxResult[1][counter] = values2[1][k];
        counter++;
        i++;
        k++;
      }
    }

    // process left elements form values1, values2 done
    while(k > 0 && values1[0].length > i &&
        values1[0][i] == values2[0][k - 1]) {
      maxResult[0][counter] = values1[0][i];
      maxResult[1][counter] = values1[1][i];
      counter++;
      i++;
    }

    // process left elements form values2, values1 done
    while(i > 0 && values2[0].length > k &&
        values2[0][k] == values1[0][i - 1]) {
      //maxResult[counter] = values2[k];
      maxResult[0][counter] = values2[0][k];
      maxResult[1][counter] = values2[1][k];
      counter++;
      k++;
    }

    if(counter == 0) {
      return null;
    }

    int[][] returnArray;
    returnArray = new int[2][counter];
    System.arraycopy(maxResult[0], 0, returnArray[0], 0, counter);
    System.arraycopy(maxResult[1], 0, returnArray[1], 0, counter);

    return returnArray;
  }
}
